package com.katesoft.scale4j.rttp.internal;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;

import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.FirebirdDialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.InformixDialect;
import org.hibernate.dialect.MySQL5InnoDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.MySQLInnoDBDialect;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.PointbaseDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SAPDBDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseASE15Dialect;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.persistent.utils.DataAccessUtility;
import com.katesoft.scale4j.rttp.hibernate.SpringHazelcastBridge;

/**
 * @author kate2007
 */
public abstract class AbstractJdbcSchemaCreator extends ResourceDatabasePopulator implements
         IRttpHazelcastBridgeAware, InitializingBean {
   public static final String TABLES_QUARTZ_ORACLE = "tables_oracle.sql";
   public static final String TABLES_QUARTZ_POSTGRESQL = "tables_postgres.sql";
   public static final String TABLES_QUARTZ_MYSQL_INNODB = "tables_mysql_innodb.sql";
   public static final String TABLES_QUARTZ_MYSQL = "tables_mysql.sql";
   public static final String TABLES_QUARTZ_DERBY = "tables_derby.sql";
   public static final String TABLES_QUARTZ_DB2 = "tables_db2.sql";
   public static final String TABLES_QUARTZ_FIREBIRD = "tables_firebird.sql";
   public static final String TABLES_QUARTZ_HSQLDB = "tables_hsqldb.sql";
   public static final String TABLES_QUARTZ_H2 = "tables_h2.sql";
   public static final String TABLES_QUARTZ_INFORMIX = "tables_informix.sql";
   public static final String TABLES_QUARTZ_POINTBASE = "tables_pointbase.sql";
   public static final String TABLES_QUARTZ_SAPDB = "tables_sapdb.sql";
   public static final String TABLES_QUARTZ_SQL_SERVER = "tables_sqlServer.sql";
   public static final String TABLES_QUARTZ_SYBASE = "tables_sybase.sql";
   //
   public static final String TABLES_BATCH_ORACLE = "schema-oracle10g.sql";
   public static final String TABLES_BATCH_POSTGRESQL = "schema-postgresql.sql";
   public static final String TABLES_BATCH_SYBASE = "schema-sybase.sql";
   public static final String TABLES_BATCH_SQL_SERVER = "schema-sqlserver.sql";
   public static final String TABLES_BATCH_MYSQL = "schema-mysql.sql";
   public static final String TABLES_BATCH_HSQLDB = "schema-hsqldb.sql";
   public static final String TABLES_BATCH_H2 = "schema-h2.sql";
   public static final String TABLES_BATCH_DB2 = "schema-db2.sql";
   public static final String TABLES_BATCH_DERBY = "schema-derby.sql";
   //
   public static final String TABLES_SPRING_INTEGRATION_DB2 = TABLES_BATCH_DB2;
   public static final String TABLES_SPRING_INTEGRATION_DERBY = TABLES_BATCH_DERBY;
   public static final String TABLES_SPRING_INTEGRATION_H2 = TABLES_BATCH_H2;
   public static final String TABLES_SPRING_INTEGRATION_HSQLDB = TABLES_BATCH_HSQLDB;
   public static final String TABLES_SPRING_INTEGRATION_MYSQL = TABLES_BATCH_MYSQL;
   public static final String TABLES_SPRING_INTEGRATION_ORACLE = TABLES_BATCH_ORACLE;
   public static final String TABLES_SPRING_INTEGRATION_POSTGRESQL = TABLES_BATCH_POSTGRESQL;
   public static final String TABLES_SPRING_INTEGRATION_SQL_SERVER = TABLES_BATCH_SQL_SERVER;
   public static final String TABLES_SPRING_INTEGRATION_SYBASE = TABLES_BATCH_SYBASE;
   //
   protected Logger logger = LogFactory.getLogger(getClass());
   protected Class<? extends Dialect> dialect;
   protected String tablesPrefix;
   protected SpringHazelcastBridge springHazelcastBridge;
   protected ClassPathResource schedulerCreationScript, batchCreationScript,
            springIntegrationCreationScript;

   /**
    * check if all tables from given collection already created/exist in database.
    * 
    * @param connection
    *           actual jdbc connection
    * @return true if all tables from given collection exists.
    * @throws java.sql.SQLException
    *            if JDBC exception occurred
    */
   public boolean tablesCreated(Connection connection) throws SQLException {
      Collection<String> tables = tables();
      return DataAccessUtility
               .allTablesExist(connection, tables.toArray(new String[tables.size()]));
   }

   /**
    * allows to set custom hibernate dialect.
    * <p/>
    * dialect is not required property and will be detected automatically if not supplied.
    * 
    * @param dialect
    *           hibernate dialect.
    */
   public void setDialect(Class<? extends Dialect> dialect) {
      this.dialect = dialect;
   }

   @Override
   @Required
   public void setRttpHazelcastBridge(SpringHazelcastBridge springHazelcastBridge) {
      this.springHazelcastBridge = springHazelcastBridge;
   }

   /**
    * specify prefix for all tables(used by this jdbc schema creator) in schema.
    * 
    * @param tablesPrefix
    *           prefix for all tables
    */
   public void setTablesPrefix(String tablesPrefix) {
      this.tablesPrefix = tablesPrefix;
   }

   /**
    * @return collection of tables that should exist after schema creation.
    */
   protected abstract Collection<String> tables();

   /**
    * @return hibernate dialect used within scale4j application.
    */
   public Class<? extends Dialect> getDialect() {
      return dialect;
   }

   @Override
   public void afterPropertiesSet() throws IOException {
      setIgnoreFailedDrops(true);
      String quartzSchemaResource = null;
      String batchSchemaResource = null;
      String springIntegrationResource = null;
      if (Oracle8iDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_ORACLE;
         batchSchemaResource = TABLES_BATCH_ORACLE;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_ORACLE;
      } else if (PostgreSQLDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_POSTGRESQL;
         batchSchemaResource = TABLES_BATCH_POSTGRESQL;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_POSTGRESQL;
      } else if (MySQLInnoDBDialect.class.isAssignableFrom(dialect)
               || MySQL5InnoDBDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_MYSQL_INNODB;
         batchSchemaResource = TABLES_BATCH_MYSQL;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_MYSQL;
      } else if (MySQLDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_MYSQL;
         batchSchemaResource = TABLES_BATCH_MYSQL;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_MYSQL;
      } else if (DerbyDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_DERBY;
         batchSchemaResource = TABLES_BATCH_DERBY;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_DERBY;
      } else if (DB2Dialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_DB2;
         batchSchemaResource = TABLES_BATCH_DB2;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_DB2;
      } else if (FirebirdDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_FIREBIRD;
      } else if (HSQLDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_HSQLDB;
         batchSchemaResource = TABLES_BATCH_HSQLDB;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_HSQLDB;
      } else if (InformixDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_INFORMIX;
      } else if (PointbaseDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_POINTBASE;
      } else if (SAPDBDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_SAPDB;
      } else if (SQLServerDialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_SQL_SERVER;
         batchSchemaResource = TABLES_BATCH_SQL_SERVER;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_SQL_SERVER;
      } else if (SybaseASE15Dialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_SYBASE;
         batchSchemaResource = TABLES_BATCH_SYBASE;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_SYBASE;
      } else if (H2Dialect.class.isAssignableFrom(dialect)) {
         quartzSchemaResource = TABLES_QUARTZ_H2;
         batchSchemaResource = TABLES_BATCH_H2;
         springIntegrationResource = TABLES_SPRING_INTEGRATION_H2;
      }
      if (quartzSchemaResource != null) {
         schedulerCreationScript = new ClassPathResource(String.format("META-INF/quartz/%s",
                  quartzSchemaResource));
         logger.info("found %s for quartz schema creation", schedulerCreationScript.getURL()
                  .toExternalForm());
      }
      if (batchSchemaResource != null) {
         batchCreationScript = new ClassPathResource(String.format(
                  "org/springframework/batch/core/%s", batchSchemaResource));
         logger.info("found %s for batch schema creation", batchCreationScript.getURL()
                  .toExternalForm());
      }
      if (springIntegrationResource != null) {
         springIntegrationCreationScript = new ClassPathResource(String.format(
                  "org/springframework/integration/jdbc/%s", springIntegrationResource));
         logger.info("found %s for spring integration message store schema creation",
                  springIntegrationCreationScript.getURL().toExternalForm());
      }
   }

   public ClassPathResource getSchedulerCreationScript() {
      return schedulerCreationScript;
   }

   public ClassPathResource getBatchCreationScript() {
      return batchCreationScript;
   }

   public ClassPathResource getSpringIntegrationCreationScript() {
      return springIntegrationCreationScript;
   }
}
